# coding: utf-8

from google.appengine.ext import db
from google.appengine.ext.db import polymodel

from accounts.models import User

class IdMixin(object):
    @property
    def id(self):
        if self.key() is None:
            return None
        return self.key().id()

class MemberAccount(db.Model, IdMixin):
    name = db.StringProperty(required=True)
    user = db.ReferenceProperty(User, required=True)
    amount = db.FloatProperty(default=0.0)
    transactions = db.IntegerProperty(default=0)
    is_active = db.BooleanProperty(default=True)
    created = db.DateTimeProperty(auto_now_add=True)

    def __init__(self, *args, **kwargs):
        user, name = kwargs.get('user'), kwargs.get('name')
        if user and not name:
            kwargs['name'] = user.name
        super(MemberAccount, self).__init__(*args, **kwargs)

    def __repr__(self):
        return '<MemberAccount: %s>' % self.name.encode('utf-8')

class SharedAccount(db.Model, IdMixin):
    name = db.StringProperty(required=True)
    creator = db.ReferenceProperty(User, required=True)
    member_ids = db.ListProperty(long)  # refer to MemberAccount
    amount = db.FloatProperty(default=0.0)
    transactions = db.IntegerProperty(default=0)
    created = db.DateTimeProperty(auto_now_add=True)

    def get_members(self):
        # TODO: cache the result
        members = []
        for mem_id in self.member_ids:
            member = MemberAccount.get_by_id(mem_id)
            members.append(member)
        return members

    def get_users(self):
        members = self.get_members()
        return [ m.user for m in members ]

    def get_user_ids(self):
        members = self.get_members()
        return [ m.user.id for m in members ]

    def add_member(self, member):
        member.put()
        self.member_ids.append(member.id)
        self.put()
        member.user.shared_account_ids.append(self.id)
        member.user.put()

    def remove_member(self, member):
        member.user.shared_account_ids.remove(self.id)
        member.user.put()
        self.member_ids.remove(member.id)
        self.put()
        member.delete()

    def __repr__(self):
        return '<SharedAccount: %s>' % self.name.encode('utf-8')

class AccountTransaction(polymodel.PolyModel, IdMixin):
    account = db.ReferenceProperty(SharedAccount, required=True)
    amount = db.FloatProperty(required=True)
    usage = db.StringProperty(required=True)
    detail = db.StringProperty(multiline=True)
    time = db.DateTimeProperty()
    creator = db.ReferenceProperty(MemberAccount, required=True)
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)

class Deposit(AccountTransaction):
    member = db.ReferenceProperty(MemberAccount, required=True)

    def save(self):
        self.put()
        self.member.amount += self.amount
        self.member.transactions += 1
        self.account.amount += self.amount
        self.member.put()
        self.account.put()

    def remove(self):
        self.delete()
        self.member.amount -= self.amount
        self.member.transactions -= 1
        self.account.amount -= self.amount
        self.member.put()
        self.account.put()

    def __repr__(self):
        return '<Deposit: member="%s", amount=%s>' % (self.member, self.amount)

class Draw(AccountTransaction):
    member = db.ReferenceProperty(MemberAccount, required=True)

    def __repr__(self):
        return '<Draw: member="%s", amount=%s>' % (self.member, self.amount)

    def save(self):
        self.put()
        self.member.amount -= self.amount
        self.member.transactions += 1
        self.account.amount -= self.amount
        self.member.put()
        self.account.put()

    def remove(self):
        self.delete()
        self.member.amount += self.amount
        self.member.transactions -= 1
        self.account.amount += self.amount
        self.account.put()

class DistributeMode(object):
    EQUAL = 'E'         # 平均分配
    PERCENT = 'P'       # 按比例分配
    NUMBER = 'N'        # 按数目分配

class Pay(AccountTransaction):
    dist_mode = db.StringProperty()
    dist_member_ids = db.ListProperty(long)
    dist_percents = db.ListProperty(int)
    dist_numbers = db.ListProperty(float)

    def get_dist_amounts(self, total_amount=None):
        if total_amount is None:
            total_amount = self.amount
        members = self.account.get_members()
        members = [ MemberAccount.get_by_id(mem_id) for mem_id in self.dist_member_ids ]
        if self.dist_mode == DistributeMode.PERCENT:
            for i, member in enumerate(members):
                percent = self.dist_percents[i]
                yield member, total_amount * percent / 100.0, percent
        elif self.dist_mode == DistributeMode.NUMBER:
            for i, member in enumerate(members):
                num = self.dist_numbers[i]
                yield member, num, num/total_amount
        else:
            for member in members:
                yield member, total_amount / len(members), 100.0 / len(members)

    def save(self):
        self.put()
        self.account.amount -= self.amount
        self.account.put()
        for member, number, _ in self.get_dist_amounts():
            member.amount -= number
            member.transactions += 1
            member.put()

    def remove(self):
        self.delete()
        self.account.amount += self.amount
        self.account.put()
        for member, number, _ in self.get_dist_amounts():
            member.amount += number
            member.transactions -= 1
            member.put()

    def __repr__(self):
        return '<Pay: amount=%s, dist_mode=%s>' % (self.amount, self.dist_mode)

class DepositAndPay(Pay):
    member = db.ReferenceProperty(MemberAccount, required=True)

    def __repr__(self):
        return '<DepositAndPay: amount=%s, dist_mode=%s>' % (self.amount, self.dist_mode)
    
    def save(self):
        self.put()
        for member, number, _ in self.get_dist_amounts():
            if member.id == self.member.id:
                member.amount += self.amount
            member.amount -= number
            member.transactions += 1
            member.put()

    def remove(self):
        self.delete()
        for member, number, _ in self.get_dist_amounts():
            if member.id == self.member.id:
                member.amount -= self.amount
            member.amount += number
            member.transactions -= 1
            member.put()

class Transfer(AccountTransaction):
    member = db.ReferenceProperty(MemberAccount, required=True)
    to_member = db.ReferenceProperty(MemberAccount, collection_name='to_transfer_set', required=True)

    def __repr__(self):
        return '<Transfer: amount=%s>' % self.amount

    def save(self):
        self.put()
        self.member.amount -= self.amount
        self.member.transactions += 1
        self.to_member.amount += self.amount
        self.to_member.transactions += 1
        self.member.save()
        self.to_member.save()

    def remove(self):
        self.delete()
        self.member.amount += self.amount
        self.member.transactions -= 1
        self.to_member.amount -= self.amount
        self.to_member.transactions -= 1
        self.member.save()
        self.to_member.save()

